package othello.gui;

import othello.gui.Othello;
import othello.model.IBoard;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class GamePanel extends JPanel implements Observer {
	private static final long serialVersionUID = 1L;
	private IBoard _board;
    /**
     * The coordinates of the upper-left square.
     * Any other square's coordinates should be inferrable from that one,
     * given that there is no padding between squares.
     */
    private Rectangle _firstSquare;
    private Color _border;
    private Color _dropIndicator;
    private int _dropRow, _dropCol;
    private Font _boldFont;
    private boolean drawBoard = true;

    public void setDrawBoard(boolean drawBoard) {
        this.drawBoard = drawBoard;
    }
    
    public GamePanel() {
        new GameGUI(); // instantiate IGui (Singleton)

        // STUB
        _border = Color.GRAY;
        _boldFont = new Font("Centrury", Font.BOLD, 14);

        // Add a listener that can begin a new game when the current game is over.
        IGui.get().addListener(new PlayAgain());
    }

    @Override
    public void paint(Graphics g) {
    	if (_board == null) {
    		return;
    	}
    	
        int rows = _board.getRowCount();
        int cols = _board.getColCount();
        Rectangle rect = getBounds();

        // We're double-buffered, so go ahead and wipe the entire background every time.
        g.setColor(_border);
        g.fillRect(0, 0, rect.width, rect.height);
        g.setFont(_boldFont);

        // Calculate the biggest square that can be used to draw all the Squares.
        // Use at least half the size of a square for a margin on all four sides.
        // TODO: subtract out vertical space for whose turn it is.
        int rowHeight = rect.height / (rows + 1);
        int colWidth = rect.width / (cols + 1);
        int squareSize = Math.min(rowHeight, colWidth);

        // Cache the coordinates of the first square, for later use.        
        _firstSquare = new Rectangle(squareSize / 2, squareSize * 3 / 4, squareSize, squareSize);

        // Display whose turn it is (or if the game is over).
        String matchScore = _board.getTournamentScore();
        g.setColor(Color.BLUE);
        if (matchScore != null) {
            g.setFont(_boldFont);
        }

        // Draw the matrix, painting unclaimed squares green.  Claimed squares
        // are painted the color associated with that player.
        if(drawBoard) {
            Rectangle sqr = new Rectangle(_firstSquare);

            // New (pretty) way to draw
            Point lastMove = _board.lastMove();
            for (int r = 0; r < rows; r++) {
                // Move back to left margin
                sqr.x = colWidth / 2;

                for (int c = 0; c < cols; c++) {
                    g.setColor(new Color(0x66, 0x99, 0x33));
                    boolean thisIsLastMove = lastMove != null
                            && lastMove.x == c && lastMove.y == r;
                    g.fill3DRect(sqr.x, sqr.y, sqr.width, sqr.height, !thisIsLastMove);

                    Color p = _board.playerAt(r, c);
                    if (p != null) {
                        g.setColor(p);
                        g.fillOval(sqr.x + 2, sqr.y + 2, sqr.width - 4, sqr.height - 4);
                    }

                    sqr.x += sqr.width;
                }

                sqr.y += sqr.height;
            }
            
            // Draw a drop indicator if one has been requested.
            if (_dropIndicator != null) {
                sqr = IGui.get().getSquare(_dropRow, _dropCol);
                if (sqr != null) {
                    for (int i = 1; i <= 4; i++) {
                        int inset = i * sqr.width / 9;
                        g.setColor(_dropIndicator);
                        g.drawOval(sqr.x + inset, sqr.y + inset,
                                sqr.width - 2 * inset, sqr.height - 2 * inset);
                    }
                }
            }

            // Draw a Pass button below the board.
            // Or if the game is over, draw a play-again button.
            sqr = IGui.get().getSquare(_board.getRowCount() - 1, _board.getColCount() / 2);
            sqr.y += sqr.height * 3 / 2;
            if (_dropRow == _board.getRowCount()) {
                g.setColor(Color.YELLOW);
            } else {
                g.setColor(Color.ORANGE);
            }
            if (!_board.isGameOver()) {
                g.drawString("PASS", sqr.x, rect.height - 2);
            } else {
                g.drawString("PLAY AGAIN", sqr.x, rect.height - 2);
            }
        }
    }

    public class GameGUI extends IGui {        
        @Override
        public void addListener(Object listener) {
            if (listener instanceof MouseListener) {
                GamePanel.this.addMouseListener((MouseListener) listener);
            }
            if (listener instanceof MouseMotionListener) {
                GamePanel.this.addMouseMotionListener((MouseMotionListener) listener);
            }
            if (listener instanceof KeyListener) {
                GamePanel.this.addKeyListener((KeyListener) listener);
            }
        }

        @Override
        public void removeListener(Object listener) {
            if (listener instanceof MouseListener) {
                GamePanel.this.removeMouseListener((MouseListener) listener);
            }
            if (listener instanceof MouseMotionListener) {
                GamePanel.this.removeMouseMotionListener((MouseMotionListener) listener);
            }
            if (listener instanceof KeyListener) {
                GamePanel.this.removeKeyListener((KeyListener) listener);
            }
        }

        @Override
        public void setBoard(IBoard board) {
            _board = board;
        }

        @Override
        public Point getSquare(Point pt) {
        	int col = (pt.x - _firstSquare.x) / _firstSquare.width;
            int row = (pt.y - _firstSquare.y) / _firstSquare.height;
            if (col < 0 || row < 0 || col >= _board.getColCount() || row >= _board.getRowCount()) {
                return null;
            }
            return new Point(col, row);
        }

        @Override
        public boolean isPassButton(Point pt) {
        	try {
	            int col = (pt.x - _firstSquare.x) / _firstSquare.width;
	            int row = (pt.y - _firstSquare.y) / _firstSquare.height;
	            // The pass button is in the middle below the last row.
	            return (col >= _board.getColCount() / 4
	                    && col < _board.getColCount() * 3 / 4
	                    && row == _board.getRowCount());
        	} catch (Exception e) {
        		return false;
        	}
        }

        @Override
        public Rectangle getSquare(int row, int col) {
            if (col < 0 || row < 0 || col >= _board.getColCount() || row >= _board.getRowCount()) {
                throw new IllegalArgumentException();
            }

            // Simply move a copy of the first rectangle over and down as necessary.
            // The first rectangle is cached from the last time we painted.
            Rectangle r = new Rectangle(_firstSquare);
            r.x += col * _firstSquare.width;
            r.y += row * _firstSquare.height;

            return r;
        }

        @Override
        public void redrawBoard() {
            repaint();
        }

        @Override
        public void showDropIndicator(Color color, int row, int col) {
            if (_dropIndicator != color
                    || row != _dropRow
                    || col != _dropCol) {
                _dropIndicator = color;
                _dropRow = row;
                _dropCol = col;
                redrawBoard();
            }
        }
    }

    public class PlayAgain extends MouseAdapter implements MouseMotionListener {
        @Override
        public void mouseClicked(MouseEvent e) {
            if (!_board.isGameOver()) {
                return;
            }
            if (!IGui.get().isPassButton(e.getPoint())) {
                return;
            }
            if (SwingUtilities.isLeftMouseButton(e)) {
                _board.beginGame();
            } else {
                // Secret trick to start a whole new tournament, with new players.
                Othello.main(null);
            }
        }

        @Override
        public void mouseMoved(MouseEvent e) {
            if (_board == null || !_board.isGameOver()) {
                return;
            }
            // Highlight the PLAY AGAIN text if we're hovering over it.
            IGui.get().showDropIndicator(null, IGui.get().isPassButton(e.getPoint()) ? _board.getRowCount() : -1, -1);
        }
    }

	@Override
	public void update(Observable obj, Object arg) {
		validate();
        repaint();
    }

	public void setBoard(IBoard board) {
		this._board = board;
	}
}
